"""
====================================================================
Copyright (C) 2012-2013 Eric Blond

This file is part of Neptune.

Neptune is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

Neptune is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with Neptune.  If not, see <http://www.gnu.org/licenses/>.
====================================================================

Created on 22 Jun 2012

@author: Eric
"""

import wx
from ..nucleus import public as nucleus
from . import utils
from . import context

#######

class Gui(nucleus.Page):
    
    def _on_Windows_change(self, old_value, new_value):
        for x in old_value:
            if x not in new_value:
                x._destroy()
        for x in new_value:
            if x not in old_value:
                x._create(self._Context)

    @nucleus.cell(settable = True, on_change = '_on_Windows_change')
    def Windows(self):
        return []

    def _on_TopWindow_change(self, value):
        if value:
            if value not in self.Windows:
                raise ValueError('Window [%s] is not in the list of windows' % value)
            elif self._Context._WxApp.GetTopWindow() != value._WxFrame:
                self._Context._WxApp.SetTopWindow(value._WxFrame)

    @nucleus.cell(settable = True, on_change = '_on_TopWindow_change')
    def TopWindow(self):
        return self.Windows[0]

    OnStart = None
    OnStop  = None

    _Context = None
        
    def _create(self, ctx):
        self._Context = context.Context(Parent = ctx, Instance = self)
        utils.call_user_cb((~self).OnStart)
        self._on_Windows_change([], self.Windows)
        self._on_TopWindow_change(self.TopWindow)
    
    def _destroy(self):
        assert self.Windows == []
        utils.call_user_cb((~self).OnStop)
        self._Context = None

#######

def scheduler():
    return nucleus.reactor().handler().Scheduler

#######

class GuiHandler(nucleus.Page):
    
    AppClass  = None
    AppKwargs = None
    Barrier   = None
    
    _AppInstance = None
    _WxApp       = None
    _WxEventLoop = None
    _Scheduler   = None
    
    @nucleus.cell
    def Gui(self):
        return self._AppInstance.Gui

    @nucleus.cell
    def ShouldStop(self):
        return not self.Gui.Windows

    @nucleus.cell
    def _Context(self):
        return context.Context(Instance = self)
    
    def start(self):
        self._AppInstance = self.AppClass(**self.AppKwargs)
        self._WxApp = wx.App()
        self._Scheduler = nucleus.Scheduler()
        self._Scheduler.start()
        self._WxEventLoop = wx.EventLoop()
        wx.EventLoop_SetActive(self._WxEventLoop)
        self.Gui._create(self._Context)
        self._Scheduler.book(0.1, self.loop)
    
    def loop(self):
        while not self.ShouldStop and self._WxEventLoop.Pending():
            self._WxEventLoop.Dispatch()
        if not self.ShouldStop:
            self._Scheduler.book(0.1, self.loop)
        else:
            self.Gui._destroy()
            self._Scheduler.stop()
            self._Scheduler = None
            self._WxApp = None
            self._AppInstance = None
            self.Barrier.send()

#######

def run(cls, **kwargs):
    barrier = nucleus.Barrier()
    reactor = nucleus.Reactor(GuiHandler)
    reactor.start(AppClass  = cls,
                  AppKwargs = kwargs,
                  Barrier   = barrier)
    reactor.handler(sync = False).start()
    barrier.wait()
    reactor.stop()

